/**
 * 演示原子变量跨cache line时的问题
 */

#include<iostream>
#include<atomic>
#include<thread>

#pragma pack(push)
#pragma pack(1)
struct TestCross
{
    alignas(64) char pad[63];
    std::atomic<int64_t> i64value;
};
#pragma pack(pop)

TestCross t;

void thread1(void)
{
    for (int i = 0; i < 10000; i++)
    {
        //0 -> 1, 1 -> 0
        auto k = t.i64value.load();
        t.i64value.store(~k);

        //t.i64value.fetch_xor(0xFFFFFFFFFFFFFFFFLL);
    }
}

int main(void)
{
    t.i64value.store(0);

    std::thread td[7];
    for (int i = 0; i < 7; i++)
    {
        td[i] = std::thread(thread1);
    }
    for (int i = 0; i < 7; i++)
    {
        td[i].join();
    }

    std::cout<< std::hex << t.i64value.load() << std::endl;
}

/***************************************
 * 对i64_value的操作是反复地取反。考虑到在load -> store中间会发生线程抢占，
 * i64_value的值最终只可能有两种情况：0，0xffff ffff ffff ffff
 * 但是实际上运行会有4种情况：

mdy@mdy-pc:~/MyGit/Cpp11Study$ ./a.out
ffffffffffffff00
mdy@mdy-pc:~/MyGit/Cpp11Study$ ./a.out
ffffffffffffffff
mdy@mdy-pc:~/MyGit/Cpp11Study$ ./a.out
ff
mdy@mdy-pc:~/MyGit/Cpp11Study$ ./a.out
ffffffffffffffff
mdy@mdy-pc:~/MyGit/Cpp11Study$ ./a.out
0
 
 * 当原子变量跨了cache line后，会发生下面这种情况：
 * 1. thread1 load cache line1 = 0x00
 * 2. thread2 store cache line1 = 0xff
 * 3. thread2 store cache line2 = 0xffff ffff ffff ff
 * 4. thread 1 load cache line2 = 0xffff ffff ffff ff
 * 这样虽然经过一个完整的store后原子变量的值是0xffff ffff ffff ffff,
 * 但thread1 load的结果却是0xffff ffff ffff ff00
 * 
 * PS:
 * 1. 如果改用fetch_xor，结果就不会出错，将始终是0
 * 2. 如果i64_value不跨cache line，即使没有按int64_t对齐，结果也只会有0和0xffff ffff ffff ffff两种情况
 * 
 * 结论：
 * 1. 不要使用#pragma pack，gcc保证默认的alignment一定不会跨cache line
 * 2. 可以放心地使用alignas，因为alignas值只能>=默认的alignment，即只能调大不能调小
 */
